/*
 * @Author: Morphlng
 * @Date: 2022-04-05 19:48:47
 * @LastEditTime: 2022-04-06 18:50:21
 * @LastEditors: Morphlng
 * @Description: Some alias for multidimension std::array
 * @FilePath: \Cish\useful_trick\nested_container.h
 */
#pragma once
#include <array>
#include <vector>
#include <iostream>

template <class T, size_t N, size_t... Ns>
struct array_helper;

template <class T, size_t N>
struct array_helper<T, N>
{
    using type = std::array<T, N>;
};

template <class T, size_t N, size_t... Ns>
struct array_helper
{
    using type = std::array<typename array_helper<T, Ns...>::type, N>;
};

template <class T, size_t... Ns>
using nested_array = typename array_helper<T, Ns...>::type;

template <class T, size_t N>
struct vector_helper;

template <class T>
struct vector_helper<T, 1>
{
    using type = std::vector<T>;
};

template <class T, size_t N>
struct vector_helper
{
    using type = std::vector<typename vector_helper<T, N - 1>::type>;
};

template <class T, size_t N>
using nested_vector = typename vector_helper<T, N>::type;

// We use 2d and 3d container more often
// So let's define some help alias for them
template <typename T, size_t Size1, size_t Size2>
using array_2d = nested_array<T, Size1, Size2>;

template <typename T, size_t Size1, size_t Size2>
std::ostream &operator<<(std::ostream &os, const std::array<std::array<T, Size2>, Size1> &arr)
{
    for (size_t i = 0; i < Size1; ++i)
    {
        for (size_t j = 0; j < Size2; ++j)
        {
            os << arr[i][j] << " ";
        }
        os << "\n";
    }
    return os;
}

template <typename T, size_t Size1, size_t Size2, size_t Size3>
using array_3d = nested_array<T, Size1, Size2, Size3>;